package gbench.sandbox.tree;

import java.util.HashMap;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiConsumer;
import org.junit.jupiter.api.Test;

import gbench.common.matlib.rdd.PVec;
import gbench.common.tree.LittleTree;
import gbench.commonApp.data.DataMatrixApp;
import gbench.commonApp.jdbc.Neo4jApp;
import static gbench.common.tree.LittleTree.IRecord.*;
import static gbench.common.tree.LittleTree.*;

/**
 * 
 * @author gbench
 *
 */
public class JunitTree extends DataMatrixApp{
    
    /**
     * 节点类
     * 
     * @author gbench
     *
     */
    static class Point extends LittleTree.Node<Integer> {
        public Point(Integer value) {
            super(value);
            for(int i=0;i<this.branches;i++) {
                this.addChild(null,true,true);
            }
        }

        /**
         * 进行节点遍历
         * @param cs 回调函数
         */
        public void traverse(BiConsumer<Point, Integer> cs) {
            LittleTree.traverse(this, cs);
        }
        
        /**
         * 修改节点返回类型
         */
        public Point getChild(int i) {
            return (Point)super.getChild(i);
        }
        
        /**
         * 把value 添加到node节点上
         * 
         * @param node  节点
         * @param value 值
         */
        public static void add(Point node, Integer value) {
            Point child = null;
            for (int i = 0; i < node.size(); i++) {
                child = node.getChild(i);
                if (child == null) { node.setChild(i, new Point(value)); return;}
                else if (child.val() >= value) break;
            } // for
            
            if(child!=null) add(child,value);
        }
        
        public void setLeft(Point p){
            this.setChild(0, p);
        }
        
        public void setRight(Point p){
            this.setChild(1, p);
        }
        
        public Point getLeft() {
            return this.getChild(0);
        }
        
        public Point getRight() {
            return this.getChild(1);
        }
        
        /**
         * 添加节点
         * @param value
         */
        public void add(Integer value) {
           add(this,value);
        }
        
        /**
         * 判断节点是否均衡
         * @return 是否均衡。
         */
        public Integer balanceFactor () {
           return BF(this);
        }
        
        /**
         * 提取节点银子
         * @param p 节点
         * @return 右子树与左子树之间的差值
         */
        public static Integer BF(Point p) {
            if(p==null)return 0;
            var left = p.getLeft();
            var right = p.getRight();
            var lheight = left == null ? 0 : left.height();
            var rheight = right == null ? 0 : right.height();
            return (lheight-rheight);
        }
        
        /**
         * 判断节点是否均衡
         * @return 是否均衡。
         */
        public boolean isBalanced() {
            return Math.abs(balanceFactor())<2;
        }
        
        /**
         * 右旋转
         * @param p
         * @return 节点的旋转
         */
        public static Point rotateR(Point p) {
            final var left = p.getLeft();
            final var p_parent = p.getParent();
            p.setLeft(left.getRight());
            left.setRight(p);
            left.setParent(p_parent);
            p.setParent(left);
            return left;
        }
        
        /**
         * 左旋转
         * @param p
         * @return 节点的旋转
         */
        public static Point rotateL(Point p) {
           final var right = p.getRight();
           final var p_parent = p.getParent();
            p.setRight(right.getLeft());// 此处会自动修改 right.getLeft() 的parent
            right.setLeft(p);// 此处会自动修改p的parent
            right.setParent(p_parent);
            p.setParent(right);
            return right;
        }
        
        // 旋转的跳帧
        public static Point adjust(final Point p) {
            // 判断树额构型
            if (BF(p) > 1) {
                if (BF(p.getLeft()) > 0) // LL 类型
                    return rotateR(p);
                else {
                    rotateL(p.getLeft().getRight());
                    return rotateR(p);
                }//if
            } else if (BF(p) < -1) {
                if (BF(p.getRight()) < 0) // RR类型
                    return rotateL(p);
                else{// RL
                    rotateR(p.getRight().getLeft());
                    return rotateL(p);
                }//if
            }//if 
            
            return p;
        }
        
        public int branches = 2;// 分叉数量
    }
    
    /**
     * 静态构造法
     * @param a
     * @return
     */
    static Point P(Integer a) {
        return new Point(a);
    }
    
    /**
     * 使用neo4j给与图像化
     */
    @Test
    public void foo() {
        final var vv = V(10, e -> P(e + 1));// 节点池
        final var ar = new AtomicReference<Point>();// 根节点
        vv.forEachCells(p -> {
            //println("add:"+p);
            if (ar.get() == null)
                ar.set(P(p.getValue()));
            else
                ar.get().add(p.getValue());
            ar.set(Point.adjust(ar.get()));
        });// 构造属性结构
        final var edge2attribues = new HashMap<IRecord,IRecord>();// 边定义集合
        final var root = ar.get();// 根节点
        final var E="E1";// 边的label
        final var elblk = "elblk";// 边label 的属性
        final var vlblk = "vlblk";// 边label 的属性
        root.traverse((point, i) -> edge2attribues.put(STRING2REC(point.getPath()), REC( "$"+vlblk,"V",  elblk,E )));// 通过节点遍历的方式 创建树形视图
        final var neo4jApp = Neo4jApp.newInstance("V",vlblk,elblk); // 创建
        final var g = neo4jApp.graph(PVec.of(edge2attribues), false);
        System.out.println(g);
        if(System.currentTimeMillis()>0) Neo4jApp // 过滤标记
        .getJdbc().withTransaction(sess->{
            sess.sqlexecute(MFT("match (a)-[e:{0}]->(b) delete a,e,b",E));//删除表
            sess.sqlexecute(MFT("create {0}",g));// 创建数据表
            final var sql = MFT("match (a)-[e:{0}]->(b) return a,e,b",E);// 查询表
            println(sql);
            var mm = sess.sql2records(sql);
            System.out.println(FMT(mm));
            sess.sqlexecute(MFT("match p = (a)-[e:{0}]->(b) foreach ( n in nodes(p) | set n.marked=TRUE )",E));
        });// withTransaction
    }

}
